#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "../inc/scanner.h"
#include "../inc/log.h"


/*
 * Type: scannerCDT
 * ----------------
 * Esta estructura es la representacion concreta del scannerADT
 * exportado por esta interface. Consiste de los siguientes campos:
 *
 * str          -- Copia del string que recibe SetScannerString
 * len          -- Longitud del string, guardado por una cuestion de eficiencia
 * cp           -- Posicion actual del cursor en el string
 * savedToken   -- String guardado por SaveToken (NULL indica que ninguno ha sido guardadp)
 * spaceOption  -- Estado de la extension space option
 */

struct scannerCDT {
	char * str;
	int len;
	int cp;
	char * savedToken;
	spaceOptionT spaceOption;
};

/* Funciones privadas */
static void SkipSpaces(scannerADT scanner);
static int ScanToEndOfIdentifier(scannerADT scanner);

scannerADT
NewScanner(void)
{
	scannerADT scanner;

	if( (scanner = malloc(sizeof(struct scannerCDT))) == NULL )
	{
		Error("NewScanner@scannerADT.c - Not enough memory to use a scanner.\n");
		return NULL;
	}

	scanner->str = NULL;
	scanner->spaceOption = IgnoreSpaces;

	return (scanner);
}

void
FreeScanner(scannerADT scanner)
{
	if (scanner->str != NULL)
		free(scanner->str);

	free(scanner);
}

void
SetScannerString (scannerADT scanner, char * str)
{
	if (scanner->str != NULL)
		free(scanner->str);

	scanner->len = strlen(str);

	if ( ( scanner->str = malloc( scanner->len+1 )) == NULL )
	{
		Error("SetScannerString@scannerADT.c - Not enough memory to scan a string.\n");
		return;
	}

	strcpy( scanner->str ,str );

	scanner->cp = 0;
	scanner->savedToken = NULL;
}

static char *
SubString(char * s, int p1, int p2)
{
	int len;
	char * result = NULL;

	if (s == NULL)
	{
		return NULL;
	}

	len = strlen(s);

	if (p1 < 0)
		p1 = 0;

	if (p2 >= len)
		p2 = len - 1;

	len = p2 - p1 + 1;

	if (len < 0)
		len = 0;

	if( (result = malloc(len+1)) == NULL ) {

		return NULL;
	}

	strncpy(result, s + p1, len);

	result[len] = 0;

	return result;
}

char *
ReadToken(scannerADT scanner)
{
	char ch;
	char * token;
	int start, finish;

	if (scanner->str == NULL)
	{
		Error("ReadToken@scannerADT.c - SetScannerString has not been called.\n");
		return NULL;
	}

	if (scanner->savedToken != NULL)
	{
		token = scanner->savedToken;
		scanner->savedToken = NULL;
		return (token);
	}

	if (scanner->spaceOption == IgnoreSpaces)
		SkipSpaces(scanner);

	start = finish = scanner->cp;

	if (start >= scanner->len)
		return NULL;

	ch = scanner->str[scanner->cp];

	if (isalnum(ch) || ch == '-' || ch == '#')
		finish = ScanToEndOfIdentifier(scanner);
	else
		scanner->cp++;

	return ( SubString(scanner->str, start, finish) );
}

int
MoreTokensExist(scannerADT scanner)
{
	if (scanner->str == NULL)
	{
		Error("MoreTokensExist@scannerADT.c - SetScannerString has not been called.\n");
		return -1;
	}

	if (scanner->savedToken != NULL)
		return (strcmp(scanner->savedToken, ""));

	if (scanner->spaceOption == IgnoreSpaces)
		SkipSpaces(scanner);

	return (scanner->cp < scanner->len);
}

void
SaveToken(scannerADT scanner, char * token)
{
	if (scanner->str == NULL)
	{
		Error("SaveToken@scannerADT.c - SetScannerString has not been called.\n");
		return;
	}

	if (scanner->savedToken != NULL)
	{
		Error("SaveToken@scannerADT.c - Token has already been saved.\n");
		return;
	}

	scanner->savedToken = token;
}

void
SetScannerSpaceOption(scannerADT scanner, spaceOptionT option)
{
	scanner->spaceOption = option;
}

spaceOptionT
GetScannerSpaceOption(scannerADT scanner)
{
	return (scanner->spaceOption);
}

/*
 * Funcion: SkipSpaces
 * Uso: SkipSpaces(scanner);
 * ---------------------------
 * Esta funcion adelanta el cursor mientras haya espacios.
 */

static void
SkipSpaces(scannerADT scanner)
{
	while (isspace(scanner->str[scanner->cp]))
	{
		scanner->cp++;
	}
}

/*
 * Funcion: ScanToEndOfIdentifier
 * Uso: finish = ScanToEndOfIdentifier(scanner);
 * -----------------------------------------------
 * Esta funcion avanza el cursor hasta formar un identificador.
 * La cual esta formada por una cadena de letras, digitos, puntos y dos puntos.
 * (Popia de Roberts)
 */

static int
ScanToEndOfIdentifier(scannerADT scanner)
{
	char ch;

	/* Aqui se especifican los caracteres que no tienen que cortar un token*/

	while ( isalnum((ch=scanner->str[scanner->cp])) || ch == '.' || ch == ':' || ch == '-' || ch == '=' ) 
	{
		scanner->cp++;
	}
	return (scanner->cp - 1);
}

